{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\nPyTorch: nn\n-----------\n\nA third order polynomial, trained to predict $y=\\sin(x)$ from $-\\pi$\nto $pi$ by minimizing squared Euclidean distance.\n\nThis implementation uses the nn package from PyTorch to build the network.\nPyTorch autograd makes it easy to define computational graphs and take gradients,\nbut raw autograd can be a bit too low-level for defining complex neural networks;\nthis is where the nn package can help. The nn package defines a set of Modules,\nwhich you can think of as a neural network layer that has produces output from\ninput and may have some trainable weights.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import torch\nimport math\n\n\n# Create Tensors to hold input and outputs.\nx = torch.linspace(-math.pi, math.pi, 2000)\ny = torch.sin(x)\n\n# For this example, the output y is a linear function of (x, x^2, x^3), so\n# we can consider it as a linear layer neural network. Let's prepare the\n# tensor (x, x^2, x^3).\np = torch.tensor([1, 2, 3])\nxx = x.unsqueeze(-1).pow(p)\n\n# In the above code, x.unsqueeze(-1) has shape (2000, 1), and p has shape\n# (3,), for this case, broadcasting semantics will apply to obtain a tensor\n# of shape (2000, 3) \n\n# Use the nn package to define our model as a sequence of layers. nn.Sequential\n# is a Module which contains other Modules, and applies them in sequence to\n# produce its output. The Linear Module computes output from input using a\n# linear function, and holds internal Tensors for its weight and bias.\n# The Flatten layer flatens the output of the linear layer to a 1D tensor,\n# to match the shape of `y`.\nmodel = torch.nn.Sequential(\n    torch.nn.Linear(3, 1),\n    torch.nn.Flatten(0, 1)\n)\n\n# The nn package also contains definitions of popular loss functions; in this\n# case we will use Mean Squared Error (MSE) as our loss function.\nloss_fn = torch.nn.MSELoss(reduction='sum')\n\nlearning_rate = 1e-6\nfor t in range(2000):\n\n    # Forward pass: compute predicted y by passing x to the model. Module objects\n    # override the __call__ operator so you can call them like functions. When\n    # doing so you pass a Tensor of input data to the Module and it produces\n    # a Tensor of output data.\n    y_pred = model(xx)\n\n    # Compute and print loss. We pass Tensors containing the predicted and true\n    # values of y, and the loss function returns a Tensor containing the\n    # loss.\n    loss = loss_fn(y_pred, y)\n    if t % 100 == 99:\n        print(t, loss.item())\n\n    # Zero the gradients before running the backward pass.\n    model.zero_grad()\n\n    # Backward pass: compute gradient of the loss with respect to all the learnable\n    # parameters of the model. Internally, the parameters of each Module are stored\n    # in Tensors with requires_grad=True, so this call will compute gradients for\n    # all learnable parameters in the model.\n    loss.backward()\n\n    # Update the weights using gradient descent. Each parameter is a Tensor, so\n    # we can access its gradients like we did before.\n    with torch.no_grad():\n        for param in model.parameters():\n            param -= learning_rate * param.grad\n\n# You can access the first layer of `model` like accessing the first item of a list\nlinear_layer = model[0]\n\n# For linear layer, its parameters are stored as `weight` and `bias`.\nprint(f'Result: y = {linear_layer.bias.item()} + {linear_layer.weight[:, 0].item()} x + {linear_layer.weight[:, 1].item()} x^2 + {linear_layer.weight[:, 2].item()} x^3')"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.6.12"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}